package functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.parsers;



import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.units.AnnotatedGene;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.units.GoTerm;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.units.Ontology;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.utils.FileUtils;

/**
 *
 * @author Jamie MacPherson
 */
public class GoaParser {

    // stores the goa file
    private File goaFile;
    // stores the Ontology
    private Ontology ontology;
    //stores the annotated genes by their gene ID
    private Map<String, AnnotatedGene> annotatedGenes;

    /**
     * Requires a file containing Gene Ontology annotataion of the genes of interest.
     * These files are available for download at the GO consortium website. Also requires
     * an <code>Ontology</code> object that contains the relationship between the annotating GO terms.
     * @param goaFile the GO annotation file.
     * @param ontology the <code>Ontology</code> object.
     */
    public GoaParser(File goaFile, Ontology ontology) {
        this.goaFile = goaFile;
        this.ontology = ontology;
        System.out.println("getting annotated genes");
        annotatedGenes = getAnnotatedGenes(ontology.getGoTerms());
    }

    /**
     * Returns all the annotated genes, the key is the gene ID and the value is the <code>AnnotatedGene</code> object.
     * @return
     */
    public Map<String, AnnotatedGene> getAnnotatedGenes() {
        return annotatedGenes;
    }

    /**
     * An internal method that reads the goa file and returns a map object containing genes
     * that are annotated with the GO terms in the given map object. Only genes that aren't tagget "NOT"
     * are considered, as these are not informative.
     * @param goTerms the map object containing go terms of interest.
     * @return a map object containing all annotated genes, the key being the gene identifier.
     */
    private Map<String, AnnotatedGene> getAnnotatedGenes(Map<Integer, GoTerm> goTerms) {

        TreeMap<String, AnnotatedGene> genes = new TreeMap<String, AnnotatedGene>();
        String thisOntologyTag = ontology.getGoOntology().getTag();

        BufferedReader reader = FileUtils.getBufferedReader(goaFile);
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                if (!line.startsWith("!")) {
                    String[] split = line.split("\t");

                    if (split[8].equals(thisOntologyTag) && !split[3].equals("NOT")) {
                        try {
                            AnnotatedGene g;
                            String gId = split[1];

                            if (genes.containsKey(gId)) {
                                g = genes.get(gId);

                            } else {
                                g = new AnnotatedGene(gId);
                                genes.put(gId, g);
                            }
                            int goTermId = Integer.parseInt(split[4].split(":")[1]);
                            if (!goTerms.containsKey(goTermId)) {

                                System.out.println("warning: your obo file does not contain the GO term " + split[4] + " that is in the annotation file, perhaps one of the files is is out of date?");
                            } else {
                                if (!g.getBaseGoTerms().contains(goTerms.get(goTermId))) {
                                    g.getBaseGoTerms().add(goTerms.get(goTermId));
                                }
                            }
                        } catch (Exception exception){
                            System.out.println("PROBLEM LINE:");
                            System.out.println(line);
                            exception.printStackTrace();
                        }
                    }
                }
            }
            reader.close();
        } catch (IOException ex) {
            Logger.getLogger(GoaParser.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("found " + genes.size() + " genes in the goa file");
        return removeGenesWithoutAnnotation(genes);
    }

    /**
     * Returns a map of annotated genes that contain all of the genes in the map passed to the
     * method minus genes that are not annotated with an informative term, i.e., a term other
     * than the root term of the ontology.
     * @param annotatedGenes a map of genes that may include genes without informative annotation
     * @return a map of genes that only includes genes with informative annotation.
     */
    public Map<String, AnnotatedGene> removeGenesWithoutAnnotation(Map<String, AnnotatedGene> annotatedGenes) {
        TreeMap<String, AnnotatedGene> truelyAnnotated = new TreeMap<String, AnnotatedGene>();

        int rootId = ontology.getGoOntology().getAccession();

        for (String id : annotatedGenes.keySet()) {
            AnnotatedGene ag = annotatedGenes.get(id);
            boolean keep = false;
            for (GoTerm gt : ag.getBaseGoTerms()) {
                if (gt.getId() != rootId) {
                    keep = true;
                }
            }
            if (keep) {
                truelyAnnotated.put(id, ag);
            }
        }
        System.out.println("found " + truelyAnnotated.size() + " annotated genes in the goa file");
        return truelyAnnotated;

    }
}
